//
//  HomeView.swift
//  Tiktok
//
//  Created by xmd on 9/30/20.
//

import SwiftUI
import AVKit
import Combine

// MARK: - Pages container

class CurrentPageIndexPublisher: ObservableObject {
    @Published var value: Int = 0
    func gt(_ n: Int) -> Bool {
        return value > n
    }
    func eq(_ n: Int) -> Bool {
        return value == n
    }
    func lt(_ n: Int) -> Bool {
        return value < n
    }
    func inc() {
        value += 1
    }
    func dec() {
        value -= 1
    }
}

struct VideoListView: View {
    @EnvironmentObject var avPlayerProvider: AVPlayerProvider
    @ObservedObject var currentPageIndex = CurrentPageIndexPublisher()
    @GestureState private var translation: CGFloat = 0
    
    private var offsetY = 0
    
    @ViewBuilder
    func GeometryCompatibleWrapper(geometry: GeometryProxy) -> some View {
        if true {
            VStack(alignment: .leading, spacing: 0) {
                ForEach(awemeData.aweme_list.indices, id: \.self) { i in
                    VideoPlayerPage()
                        .environmentObject(currentPageIndex)
                        .environmentObject(VideoPlayerPageVM(index: i))
                        .cornerRadius(4)
                }
                .frame(width: geometry.size.width, height: geometry.size.height)
            }
        }
    }
    
    var body: some View {
        GeometryReader { geometry in
            GeometryCompatibleWrapper(geometry: geometry)
                .offset(y: -CGFloat(self.currentPageIndex.value) *  geometry.size.height + self.translation)
                .animation(.interactiveSpring())
                .gesture(
                    DragGesture()
                        .updating($translation, body: { (value, state, _) in
                            state = value.translation.height
                        })
                        .onEnded { value in
                            let offset = value.predictedEndTranslation.height / geometry.size.height
                            let newIndex = (CGFloat(self.currentPageIndex.value) - offset).rounded()
                            let predictedIndex = min(max(Int(newIndex), 0), awemeData.aweme_list.count - 1)
                            if self.currentPageIndex.lt(predictedIndex) {
                                self.currentPageIndex.inc()
                            } else if self.currentPageIndex.gt(predictedIndex) {
                                self.currentPageIndex.dec()
                            }
                        }
                )
        }
    }
}

// MARK: - Single video player page

struct VideoPlayerPage: View {
    @EnvironmentObject var model: VideoPlayerPageVM
    @EnvironmentObject var avPlayerProvider: AVPlayerProvider
    @EnvironmentObject var currentPageIndexPublisher: CurrentPageIndexPublisher
    @State var showProfileView = false
    
    var player: AVPlayer? {
        avPlayerProvider.get(forPage: model.index, from: model.videoUrl)
    }
    
    var body: some View {
        ZStack {
            VideoPlayer()
                .onTapGesture(perform: playOrPause)
            PlayerForeground(showProfileView: .init(
                get: { showProfileView },
                set: {
                    newValue in
                    showProfileView = newValue
                    if showProfileView {
                        player?.pause()
                        player?.seek(to: CMTime.zero)
                    } else {
                        model.isPaused = false
                        if let status = player?.timeControlStatus {
                            if status != .playing {
                                player?.play()
                            }
                        }
                    }
                })
            )
            if self.model.isPaused {
                Image("icon_play_pause52x62")
                    .transition(AnyTransition.scale(scale: 2).combined(with: AnyTransition.opacity))
                    .onTapGesture(perform: playOrPause)
            }
        }.onReceive(currentPageIndexPublisher.$value) {currentPageIndex in
            if currentPageIndex == model.index {
                model.isPaused = false
                player?.play()
            } else {
                player?.pause()
                player?.seek(to: CMTime.zero)
            }
        }
    }
    
    func playOrPause() {
        model.playOrPause()
        if model.isPaused {
            player?.pause()
        } else {
            player?.play()
        }
    }
}

// MARK: - Foreground View

struct PlayerForeground: View {
    @EnvironmentObject var model : VideoPlayerPageVM
    @EnvironmentObject var imageProvider: ImageProvider
    @State var angle: Double = 0
    @Binding var showProfileView: Bool
    
    var body: some View {
        VStack {
            Spacer()
            HStack {
                Spacer()
                VStack {
                    NavigationLink(destination: ProfileView(), isActive: _showProfileView) {
                        OptionalImage(uiImage: imageProvider.get(from: model.avatarImageUrl))
                            .frame(width: 50, height: 50)
                            .clipShape(Circle())
                            .overlay(Circle().stroke(Color.white, lineWidth: 2))
                    }.buttonStyle(PlainButtonStyle())
                    LikeButton()
                        .frame(width: 50, height: 50)
                        .onTapGesture {
                            model.isLiked.toggle()
                        }
                    Text(model.diggCount.toCompactFormat())
                        .foregroundColor(Color.white)
                        .font(.system(size: PlayerForeground.countTextSize))
                    Image("icon_home_comment40x40")
                        .frame(width: 50, height: 50)
                    Text(model.commentCount.toCompactFormat())
                        .foregroundColor(Color.white)
                        .font(.system(size: PlayerForeground.countTextSize))
                    Image("icon_home_share40x40")
                        .frame(width: 50, height: 50)
                    Text(model.shareCount.toCompactFormat())
                        .foregroundColor(Color.white)
                        .font(.system(size: PlayerForeground.countTextSize))
                }.padding(.trailing, 16)
            }
            HStack {
                VStack(alignment: .leading, spacing: 10) {
                    Text("@\(model.nickName)")
                        .font(.system(size: 14, weight: Font.Weight.bold))
                    Text(model.desc)
                        .font(.system(size: 14))
                    HStack {
                        Image("icon_home_musicnote3")
                            .frame(width: 20, height: 20)
                        Text(model.musicDesc)
                            .font(.system(size: 12))
                    }
                }
                .foregroundColor(.white)
                Spacer(minLength: 80)
                ZStack {
                    if !model.isPaused {
                        Group {
                            FloatingUpNote(imgSrc: "icon_home_musicnote1", delay: 0)
                            FloatingUpNote(imgSrc: "icon_home_musicnote2", delay: 1)
                        }.onAppear {
                            withAnimation(Animation.linear(duration: 3).repeatForever(autoreverses: false)) {
                                angle = 360
                            }
                        }
                    }
                    ZStack {
                        Image("music_cover")
                            .frame(width: 50, height: 50)
                        OptionalImage(uiImage: imageProvider.get(from: model.musicCoverImageUrl))
                            .frame(width: 20, height: 20)
                            .clipShape(Circle())
                    }
                    .opacity(model.isPaused ? 1 : 0)
                    .animation(nil)
                    ZStack {
                        Image("music_cover")
                            .frame(width: 50, height: 50)
                        OptionalImage(uiImage: imageProvider.get(from: model.musicCoverImageUrl))
                            .frame(width: 20, height: 20)
                            .clipShape(Circle())
                    }
                    .opacity(model.isPaused ? 0 : 1)
                    .animation(nil)
                    .rotationEffect(.degrees(angle))
                    .animation(Animation.linear(duration: 3).repeatForever(autoreverses: false), value: angle)
                }
            }
            .padding(.horizontal, 16)
        }
    }
    
    private static var countTextSize: CGFloat =  12
}

struct FloatingUpNote: View {
    var imgSrc: String
    var delay: Double = 0
    @State private var opacity: Double = 0.1
    @State private var scale: CGFloat = 1
    @State private var rotation: Double = 0
    @State private var rotation2: Double = 0
    var body: some View {
        Image(imgSrc)
            .resizable()
            .frame(width: 12, height: 12)
            .opacity(opacity)
            .scaleEffect(scale)
            .rotationEffect(.degrees(rotation))
            .rotationEffect(.degrees(rotation2), anchor: UnitPoint(x: 0.2, y:-3))
            .animation(Animation.linear(duration: 2).repeatForever(autoreverses: false).delay(delay), value: rotation)
            .onAppear {
//                withAnimation(Animation.linear(duration: 2).repeatForever(autoreverses: false).delay(delay)) {
                    rotation = -80
                    rotation2 = 120
                    scale = 1.4
//                }
//                withAnimation(Animation.linear(duration: 1).repeatForever(autoreverses: true).delay(delay)) {
                    opacity = 0.8
//                }
            }
    }
}



// MARK: - Video player

struct VideoPlayer : UIViewRepresentable {
    @EnvironmentObject var model: VideoPlayerPageVM
    @EnvironmentObject var avPlayerProvider: AVPlayerProvider
    
    func makeUIView(context: Context) -> some UIView {
        let view = PlayerHostUIView()
        view.playerLayer.player = avPlayerProvider.get(forPage: model.index, from: model.videoUrl)
        view.playerLayer.videoGravity = .resizeAspectFill
        return view
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        
    }
}

class PlayerHostUIView : UIView {
    override class var layerClass: AnyClass {
        return AVPlayerLayer.self
    }
    
    var playerLayer: AVPlayerLayer {
        return layer as! AVPlayerLayer
    }
}


extension Int {
    func toCompactFormat() -> String {
        if self >= 10000 {
            return String(format: "%.1f",  (Double(self) / 10000.0)) + "w"
        } else if self >= 1000 {
            return String(format: "%.1f",  (Double(self) / 1000.0)) + "k"
        } else {
            return self.description
        }
    }
}

extension AnyTransition {
    static var moveAndFade: AnyTransition {
        let insertion = AnyTransition.move(edge: .trailing)
            .combined(with: .opacity)
        let removal = AnyTransition.scale
            .combined(with: .opacity)
        return .asymmetric(insertion: insertion, removal: removal)
    }
}
